//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<half.h>
///	@path	~/src/lib/math/
///	@date	2007/09/29
///	@desc	half -- a 16-bit floating point number class, from ILMBase.

#pragma once

#include "xeres.h"

namespace xeres {

//---------------------------------------------------------------------------
//
//	half -- a 16-bit floating point number class:
//
//	Type half can represent positive and negative numbers whose
//	magnitude is between roughly 6.1e-5 and 6.5e+4 with a relative
//	error of 9.8e-4; numbers smaller than 6.1e-5 can be represented
//	with an absolute error of 6.0e-8.  All integers from -2048 to
//	+2048 can be represented exactly.
//
//	Type half behaves (almost) like the built-in C++ floating point
//	types.  In arithmetic expressions, half, float and double can be
//	mixed freely.  Here are a few examples:
//
//	    half a (3.5);
//	    float b (a + sqrt (a));
//	    a += b;
//	    b += a;
//	    b = a + 7;
//
//	Conversions from half to float are lossless; all half numbers
//	are exactly representable as floats.
//
//	Conversions from float to half may not preserve the float's
//	value exactly.  If a float is not representable as a half, the
//	float value is rounded to the nearest representable half.  If
//	a float value is exactly in the middle between the two closest
//	representable half values, then the float value is rounded to
//	the half with the greater magnitude.
//
//	Overflows during float-to-half conversions cause arithmetic
//	exceptions.  An overflow occurs when the float value to be
//	converted is too large to be represented as a half, or if the
//	float value is an infinity or a NAN.
//
//	The implementation of type half makes the following assumptions
//	about the implementation of the built-in C++ types:
//
//	    float is an IEEE 754 single-precision number
//	    sizeof (float) == 4
//	    sizeof (unsigned int) == sizeof (float)
//	    alignof (unsigned int) == alignof (float)
//	    sizeof (unsigned short) == 2
//
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
//
// Implementation --
//
// Representation of a float:
//
//	We assume that a float, f, is an IEEE 754 single-precision
//	floating point number, whose bits are arranged as follows:
//
//	    31 (msb)
//	    | 
//	    | 30     23
//	    | |      | 
//	    | |      | 22                    0 (lsb)
//	    | |      | |                     |
//	    X XXXXXXXX XXXXXXXXXXXXXXXXXXXXXXX
//
//	    s e        m
//
//	S is the sign-bit, e is the exponent and m is the significand.
//
//	If e is between 1 and 254, f is a normalized number:
//
//	            s    e-127
//	    f = (-1)  * 2      * 1.m
//
//	If e is 0, and m is not zero, f is a denormalized number:
//
//	            s    -126
//	    f = (-1)  * 2      * 0.m
//
//	If e and m are both zero, f is zero:
//
//	    f = 0.0
//
//	If e is 255, f is an "infinity" or "not a number" (NAN),
//	depending on whether m is zero or not.
//
//	Examples:
//
//	    0 00000000 00000000000000000000000 = 0.0
//	    0 01111110 00000000000000000000000 = 0.5
//	    0 01111111 00000000000000000000000 = 1.0
//	    0 10000000 00000000000000000000000 = 2.0
//	    0 10000000 10000000000000000000000 = 3.0
//	    1 10000101 11110000010000000000000 = -124.0625
//	    0 11111111 00000000000000000000000 = +infinity
//	    1 11111111 00000000000000000000000 = -infinity
//	    0 11111111 10000000000000000000000 = NAN
//	    1 11111111 11111111111111111111111 = NAN
//
// Representation of a half:
//
//	Here is the bit-layout for a half number, h:
//
//	    15 (msb)
//	    | 
//	    | 14  10
//	    | |   |
//	    | |   | 9        0 (lsb)
//	    | |   | |        |
//	    X XXXXX XXXXXXXXXX
//
//	    s e     m
//
//	S is the sign-bit, e is the exponent and m is the significand.
//
//	If e is between 1 and 30, h is a normalized number:
//
//	            s    e-15
//	    h = (-1)  * 2     * 1.m
//
//	If e is 0, and m is not zero, h is a denormalized number:
//
//	            S    -14
//	    h = (-1)  * 2     * 0.m
//
//	If e and m are both zero, h is zero:
//
//	    h = 0.0
//
//	If e is 31, h is an "infinity" or "not a number" (NAN),
//	depending on whether m is zero or not.
//
//	Examples:
//
//	    0 00000 0000000000 = 0.0
//	    0 01110 0000000000 = 0.5
//	    0 01111 0000000000 = 1.0
//	    0 10000 0000000000 = 2.0
//	    0 10000 1000000000 = 3.0
//	    1 10101 1111000001 = -124.0625
//	    0 11111 0000000000 = +infinity
//	    1 11111 0000000000 = -infinity
//	    0 11111 1000000000 = NAN
//	    1 11111 1111111111 = NAN
//
// Conversion:
//
//	Converting from a float to a half requires some non-trivial bit
//	manipulations.  In some cases, this makes conversion relatively
//	slow, but the most common case is accelerated via table lookups.
//
//	Converting back from a half to a float is easier because we don't
//	have to do any rounding.  In addition, there are only 65536
//	different half numbers; we can convert each of those numbers once
//	and store the results in a table.  Later, all conversions can be
//	done using only simple table lookups.
//
//---------------------------------------------------------------------------

//-------------------------------------------------------------------------
// Limits
//
// Visual C++ will complain if HALF_MIN, HALF_NRM_MIN etc. are not float
// constants, but at least one other compiler (gcc 2.96) produces incorrect
// results if they are.
//-------------------------------------------------------------------------

#if (defined _WIN32 || defined _WIN64) && defined _MSC_VER
	// Smallest positive half
  	#define HALF_MIN						5.96046448e-08f	
	// Smallest positive normalized half
  	#define HALF_NRM_MIN					6.10351562e-05f
	// Largest positive half
  	#define HALF_MAX						65504.0f
	// Smallest positive e for which
	// half (1.0 + e) != half (1.0)
  	#define HALF_EPSILON					0.00097656f
#else
	// Smallest positive half
  	#define HALF_MIN						5.96046448e-08	
	// Smallest positive normalized half
  	#define HALF_NRM_MIN					6.10351562e-05	
	// Largest positive half
  	#define HALF_MAX						65504.0
	// Smallest positive e for which
	// half (1.0 + e) != half (1.0)
  	#define HALF_EPSILON					0.00097656
#endif

// Number of digits in mantissa
// (significand + hidden leading 1)
#define HALF_MANT_DIG						11		
// Number of base 10 digits that
// can be represented without change
#define HALF_DIG							2
// Base of the exponent
#define HALF_RADIX							2
// Minimum negative integer such that
// HALF_RADIX raised to the power of
// one less than that integer is a
// normalized half
#define HALF_MIN_EXP						-13
// Maximum positive integer such that
// HALF_RADIX raised to the power of
// one less than that integer is a
// normalized half
#define HALF_MAX_EXP						16
// Minimum positive integer such
// that 10 raised to that power is
// a normalized half
#define HALF_MIN_10_EXP						-4
// Maximum positive integer such
// that 10 raised to that power is
// a normalized half
#define HALF_MAX_10_EXP						4

	/*!
		\class	half
		\brief	16-bit floating point number class.
	*/
	class half
	{
	public:

		/// \ctor
		half( void );

		half( const float f );

		// copier
		half( const half& rhs );

	// constant value

		/*!
			\brief
		*/
		static half to_half( const float f );

		/*!
			\brief	Returns positive infinite value.
		*/
		static half positive_infinite( void );

		/*!
			\brief	Returns negative infinite value.
		*/
		static half negative_infinite( void );

		/*!
			\brief	Returns a NAN with the bit pattern 0111111111111111.
		*/
		static half q_nan( void );

		/*!
			\brief	Returns a NAN with the bit pattern 0111110111111111.
		*/
		static half s_nan( void );

	// methods

		/*!
			\brief	Round to n-bit precision (n should be between 0 and 10).
					After rounding, the significand's 10-n least significant
					bits will be zero.
		*/
		half round( const uint n ) const;

		/*!
			\brief	Returns true if h is a normalized number,
					a denormalized number or zero.
		*/
		bool is_finite( void ) const;

		/*!
			\brief	Returns true if half is a positive or negative infinity,
		*/
		bool is_infinite( void ) const;

		/*!
			\brief	Returns true if half is a normalized number.
		*/
		bool is_normalized( void ) const;

		/*!
			\brief	Returns true if half is a denormalized number.
		*/
		bool is_denormalized( void ) const;

		/*!
			\brief	Returns true if half is zero.
		*/
		bool is_zero( void ) const;

		/*!
			\brief	Returns true if the sign bit of half is set (negative).
		*/
		bool is_negative( void ) const;

		/*!
			\brief	Returns true if half is NAN.
		*/
		bool is_nan( void ) const;

		/*!
			\brief
		*/
		ushort get_bits( void ) const;

		/*!
			\brief
		*/
		void set_bits( ushort bits );


	// operators

		/*!
			\brief		Fp32 conversion.
		*/
		operator float( void ) const;

		/*!
			\brief		Negative.
		*/
		half operator - ( void ) const;

		// assignment

		half& operator = ( half h );
		half& operator = ( float f );

		half& operator += ( half f );
		half& operator += ( float f );

		half& operator -= ( half f );
		half& operator -= ( float f );

		half& operator *= ( half f );
		half& operator *= ( float f );

		half& operator /= ( half f );
		half& operator /= ( float f );

	// intermediate representation

		union uif
		{
			uint	i;
			float	f;
		};

	private:

		/*!
			\brief
		*/
		static short convert( int i );

		/*!
			\brief
		*/
		static float overflow( void );

	// internal representation

		ushort	m_h;

		static const uif		s_toFloat[ 1 <<16 ];
		static const ushort		s_eLut[ 1 << 9 ];

#if XERES_MATH_GENERATE_HALF_TABLE
	// generate
		void generate_to_float( void );
		void generate_e_lut( void );
#endif // XERES_MATH_GENERATE_HALF_TABLE

	};

} // namespace xeres

// inline
#include "lib/math/half.inl"

#if	XERES_MATH_USING_HALF_AS_GLOBAL
using xeres::half;
#endif // XERES_MATH_USING_HALF_AS_GLOBAL
